home *** CD-ROM | disk | FTP | other *** search
/ Risc World 5 / Risc World 5.iso / SOFTWARE / Issue5 / PD / DIRSYNC / LegalStuff / gnudiff / sdiff.c < prev    next >
C/C++ Source or Header  |  2004-12-19  |  27KB  |  1,249 lines

  1. /* sdiff - side-by-side merge of file differences
  2.  
  3.    Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 2001, 2002 Free
  4.    Software Foundation, Inc.
  5.  
  6.    This file is part of GNU DIFF.
  7.  
  8.    GNU DIFF is free software; you can redistribute it and/or modify
  9.    it under the terms of the GNU General Public License as published by
  10.    the Free Software Foundation; either version 2, or (at your option)
  11.    any later version.
  12.  
  13.    GNU DIFF is distributed in the hope that it will be useful,
  14.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  15.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  16.    See the GNU General Public License for more details.
  17.  
  18.    You should have received a copy of the GNU General Public License
  19.    along with this program; see the file COPYING.
  20.    If not, write to the Free Software Foundation,
  21.    59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
  22.  
  23. #include "system.h"
  24.  
  25. #include <c-stack.h>
  26. #ifdef __riscos
  27. #include "basename.h"
  28. #else
  29. #include <dirname.h>
  30. #endif
  31. #include <error.h>
  32. #include <exitfail.h>
  33. #include <freesoft.h>
  34. #include <getopt.h>
  35. #include <quotesys.h>
  36. #include <stdio.h>
  37. #include <xalloc.h>
  38.  
  39. static char const authorship_msgid[] = N_("Written by Thomas Lord.");
  40.  
  41. static char const copyright_string[] =
  42.   "Copyright (C) 2002 Free Software Foundation, Inc.";
  43.  
  44. extern char const version_string[];
  45.  
  46. /* Size of chunks read from files which must be parsed into lines.  */
  47. #define SDIFF_BUFSIZE ((size_t) 65536)
  48.  
  49. char *program_name;
  50.  
  51. extern const char *filename_lastdirchar(const char *const);
  52.  
  53. static char const *editor_program = DEFAULT_EDITOR_PROGRAM;
  54. static char const **diffargv;
  55.  
  56. static char * volatile tmpname;
  57. static FILE *tmp;
  58.  
  59. #if HAVE_WORKING_FORK || HAVE_WORKING_VFORK
  60. static pid_t volatile diffpid;
  61. #endif
  62.  
  63. struct line_filter;
  64.  
  65. static RETSIGTYPE catchsig (int);
  66. static bool edit (struct line_filter *, char const *, lin, lin, struct line_filter *, char const *, lin, lin, FILE *);
  67. static bool interact (struct line_filter *, struct line_filter *, char const *, struct line_filter *, char const *, FILE *);
  68. static void checksigs (void);
  69. static void diffarg (char const *);
  70. static void fatal (char const *) __attribute__((noreturn));
  71. static void perror_fatal (char const *) __attribute__((noreturn));
  72. static void trapsigs (void);
  73. static void untrapsig (int);
  74.  
  75. #define NUM_SIGS (sizeof sigs / sizeof *sigs)
  76. static int const sigs[] = {
  77. #ifdef SIGHUP
  78.        SIGHUP,
  79. #endif
  80. #ifdef SIGQUIT
  81.        SIGQUIT,
  82. #endif
  83. #ifdef SIGTERM
  84.        SIGTERM,
  85. #endif
  86. #ifdef SIGXCPU
  87.        SIGXCPU,
  88. #endif
  89. #ifdef SIGXFSZ
  90.        SIGXFSZ,
  91. #endif
  92.        SIGINT,
  93. #ifdef SIGPIPE
  94.        SIGPIPE
  95. #endif
  96. };
  97. #define handler_index_of_SIGINT (NUM_SIGS - 2)
  98. #define handler_index_of_SIGPIPE (NUM_SIGS - 1)
  99.  
  100. #if HAVE_SIGACTION
  101.   /* Prefer `sigaction' if available, since `signal' can lose signals.  */
  102.   static struct sigaction initial_action[NUM_SIGS];
  103. # define initial_handler(i) (initial_action[i].sa_handler)
  104.   static void signal_handler (int, RETSIGTYPE (*) (int));
  105. #else
  106.   static RETSIGTYPE (*initial_action[NUM_SIGS]) ();
  107. # define initial_handler(i) (initial_action[i])
  108. # define signal_handler(sig, handler) signal (sig, handler)
  109. #endif
  110.  
  111. #if ! HAVE_SIGPROCMASK
  112. # define sigset_t int
  113. # define sigemptyset(s) (*(s) = 0)
  114. # ifndef sigmask
  115. #  define sigmask(sig) (1 << ((sig) - 1))
  116. # endif
  117. # define sigaddset(s, sig) (*(s) |= sigmask (sig))
  118. # ifndef SIG_BLOCK
  119. #  define SIG_BLOCK 0
  120. # endif
  121. # ifndef SIG_SETMASK
  122. #  define SIG_SETMASK (! SIG_BLOCK)
  123. # endif
  124. # define sigprocmask(how, n, o) \
  125.     ((how) == SIG_BLOCK ? *(o) = sigblock (*(n)) : sigsetmask (*(n)))
  126. #endif
  127.  
  128. static bool diraccess (char const *);
  129. static int temporary_file (void);
  130.  
  131. /* Options: */
  132.  
  133. /* Name of output file if -o specified.  */
  134. static char const *output;
  135.  
  136. /* Do not print common lines.  */
  137. static bool suppress_common_lines;
  138.  
  139. /* Value for the long option that does not have single-letter equivalents.  */
  140. enum
  141. {
  142.   DIFF_PROGRAM_OPTION = CHAR_MAX + 1,
  143.   HELP_OPTION,
  144.   STRIP_TRAILING_CR_OPTION
  145. };
  146.  
  147. static struct option const longopts[] =
  148. {
  149.   {"diff-program", 1, 0, DIFF_PROGRAM_OPTION},
  150.   {"expand-tabs", 0, 0, 't'},
  151.   {"help", 0, 0, HELP_OPTION},
  152.   {"ignore-all-space", 0, 0, 'W'}, /* swap W and w for historical reasons */
  153.   {"ignore-blank-lines", 0, 0, 'B'},
  154.   {"ignore-case", 0, 0, 'i'},
  155.   {"ignore-matching-lines", 1, 0, 'I'},
  156.   {"ignore-space-change", 0, 0, 'b'},
  157.   {"ignore-tab-expansion", 0, 0, 'E'},
  158.   {"left-column", 0, 0, 'l'},
  159.   {"minimal", 0, 0, 'd'},
  160.   {"output", 1, 0, 'o'},
  161.   {"speed-large-files", 0, 0, 'H'},
  162.   {"strip-trailing-cr", 0, 0, STRIP_TRAILING_CR_OPTION},
  163.   {"suppress-common-lines", 0, 0, 's'},
  164.   {"text", 0, 0, 'a'},
  165.   {"version", 0, 0, 'v'},
  166.   {"width", 1, 0, 'w'},
  167.   {0, 0, 0, 0}
  168. };
  169.  
  170. static void try_help (char const *, char const *) __attribute__((noreturn));
  171. static void
  172. try_help (char const *reason_msgid, char const *operand)
  173. {
  174.   if (reason_msgid)
  175.     error (0, 0, _(reason_msgid), operand);
  176.   error (EXIT_TROUBLE, 0, _("Try `%s --help' for more information."),
  177.      program_name);
  178.   abort ();
  179. }
  180.  
  181. static void
  182. check_stdout (void)
  183. {
  184.   if (ferror (stdout))
  185.     fatal ("write failed");
  186.   else if (fclose (stdout) != 0)
  187.     perror_fatal (_("standard output"));
  188. }
  189.  
  190. static char const * const option_help_msgid[] = {
  191.   N_("-o FILE  --output=FILE  Operate interactively, sending output to FILE."),
  192.   "",
  193.   N_("-i  --ignore-case  Consider upper- and lower-case to be the same."),
  194.   N_("-E  --ignore-tab-expansion  Ignore changes due to tab expansion."),
  195.   N_("-b  --ignore-space-change  Ignore changes in the amount of white space."),
  196.   N_("-W  --ignore-all-space  Ignore all white space."),
  197.   N_("-B  --ignore-blank-lines  Ignore changes whose lines are all blank."),
  198.   N_("-I RE  --ignore-matching-lines=RE  Ignore changes whose lines all match RE."),
  199.   N_("--strip-trailing-cr  Strip trailing carriage return on input."),
  200.   N_("-a  --text  Treat all files as text."),
  201.   "",
  202.   N_("-w NUM  --width=NUM  Output at most NUM (default 130) columns per line."),
  203.   N_("-l  --left-column  Output only the left column of common lines."),
  204.   N_("-s  --suppress-common-lines  Do not output common lines."),
  205.   "",
  206.   N_("-t  --expand-tabs  Expand tabs to spaces in output."),
  207.   "",
  208.   N_("-d  --minimal  Try hard to find a smaller set of changes."),
  209.   N_("-H  --speed-large-files  Assume large files and many scattered small changes."),
  210.   N_("--diff-program=PROGRAM  Use PROGRAM to compare files."),
  211.   "",
  212.   N_("-v  --version  Output version info."),
  213.   N_("--help  Output this help."),
  214.   0
  215. };
  216.  
  217. static void
  218. usage (void)
  219. {
  220.   char const * const *p;
  221.  
  222.   printf (_("Usage: %s [OPTION]... FILE1 FILE2\n"), program_name);
  223.   printf ("%s\n\n", _("Side-by-side merge of file differences."));
  224.   for (p = option_help_msgid;  *p;  p++)
  225.     if (**p)
  226.       printf ("  %s\n", _(*p));
  227.     else
  228.       putchar ('\n');
  229.   printf ("\n%s\n\n%s\n",
  230.       _("If a FILE is `-', read standard input."),
  231.       _("Report bugs to <bug-gnu-utils@gnu.org>."));
  232. }
  233.  
  234. static void
  235. cleanup (void)
  236. {
  237. #if HAVE_WORKING_FORK || HAVE_WORKING_VFORK
  238.   if (0 < diffpid)
  239.     kill (diffpid, SIGPIPE);
  240. #endif
  241.   if (tmpname)
  242.     unlink (tmpname);
  243. }
  244.  
  245. static void exiterr (void) __attribute__((noreturn));
  246. static void
  247. exiterr (void)
  248. {
  249.   cleanup ();
  250.   untrapsig (0);
  251.   checksigs ();
  252.   exit (EXIT_TROUBLE);
  253. }
  254.  
  255. static void
  256. fatal (char const *msgid)
  257. {
  258.   error (0, 0, "%s", _(msgid));
  259.   exiterr ();
  260. }
  261.  
  262. static void
  263. perror_fatal (char const *msg)
  264. {
  265.   int e = errno;
  266.   checksigs ();
  267.   error (0, e, "%s", msg);
  268.   exiterr ();
  269. }
  270.  
  271. static void
  272. ck_editor_status (int errnum, int status)
  273. {
  274.   if (errnum | status)
  275.     {
  276.       char const *failure_msgid = N_("subsidiary program `%s' failed");
  277.       if (! errnum && WIFEXITED (status))
  278.     switch (WEXITSTATUS (status))
  279.       {
  280.       case 126:
  281.         failure_msgid = N_("subsidiary program `%s' not executable");
  282.         break;
  283.       case 127:
  284.         failure_msgid = N_("subsidiary program `%s' not found");
  285.         break;
  286.       }
  287.       error (0, errnum, _(failure_msgid), editor_program);
  288.       exiterr ();
  289.     }
  290. }
  291.  
  292. static FILE *
  293. ck_fopen (char const *fname, char const *type)
  294. {
  295.   FILE *r = fopen (fname, type);
  296.   if (! r)
  297.     perror_fatal (fname);
  298.   return r;
  299. }
  300.  
  301. static void
  302. ck_fclose (FILE *f)
  303. {
  304.   if (fclose (f))
  305.     perror_fatal ("fclose");
  306. }
  307.  
  308. static size_t
  309. ck_fread (char *buf, size_t size, FILE *f)
  310. {
  311.   size_t r = fread (buf, sizeof (char), size, f);
  312.   if (r == 0 && ferror (f))
  313.     perror_fatal (_("read failed"));
  314.   return r;
  315. }
  316.  
  317. static void
  318. ck_fwrite (char const *buf, size_t size, FILE *f)
  319. {
  320.   if (fwrite (buf, sizeof (char), size, f) != size)
  321.     perror_fatal (_("write failed"));
  322. }
  323.  
  324. static void
  325. ck_fflush (FILE *f)
  326. {
  327.   if (fflush (f) != 0)
  328.     perror_fatal (_("write failed"));
  329. }
  330.  
  331. static char const *
  332. expand_name (char *name, bool is_dir, char const *other_name)
  333. {
  334.   if (strcmp (name, "-") == 0)
  335.     fatal ("cannot interactively merge standard input");
  336.   if (! is_dir)
  337.     return name;
  338.   else
  339.     {
  340.       /* Yield NAME/BASE, where BASE is OTHER_NAME's basename.  */
  341.       char const *base = base_name (other_name);
  342.       size_t namelen = strlen (name), baselen = strlen (base);
  343. #ifdef __riscos
  344.       bool insert_slash = *base_name (name) && name[namelen - 1] != '.';
  345. #else
  346.       bool insert_slash = *base_name (name) && name[namelen - 1] != '/';
  347. #endif
  348.       char *r = xmalloc (namelen + insert_slash + baselen + 1);
  349.       memcpy (r, name, namelen);
  350. #ifdef __riscos
  351.       r[namelen] = '.';
  352. #else
  353.       r[namelen] = '/';
  354. #endif
  355.       memcpy (r + namelen + insert_slash, base, baselen + 1);
  356.       return r;
  357.     }
  358. }
  359.  
  360.  
  361.  
  362. struct line_filter {
  363.   FILE *infile;
  364.   char *bufpos;
  365.   char *buffer;
  366.   char *buflim;
  367. };
  368.  
  369. static void
  370. lf_init (struct line_filter *lf, FILE *infile)
  371. {
  372.   lf->infile = infile;
  373.   lf->bufpos = lf->buffer = lf->buflim = xmalloc (SDIFF_BUFSIZE + 1);
  374.   lf->buflim[0] = '\n';
  375. }
  376.  
  377. /* Fill an exhausted line_filter buffer from its INFILE */
  378. static size_t
  379. lf_refill (struct line_filter *lf)
  380. {
  381.   size_t s = ck_fread (lf->buffer, SDIFF_BUFSIZE, lf->infile);
  382.   lf->bufpos = lf->buffer;
  383.   lf->buflim = lf->buffer + s;
  384.   lf->buflim[0] = '\n';
  385.   checksigs ();
  386.   return s;
  387. }
  388.  
  389. /* Advance LINES on LF's infile, copying lines to OUTFILE */
  390. static void
  391. lf_copy (struct line_filter *lf, lin lines, FILE *outfile)
  392. {
  393.   char *start = lf->bufpos;
  394.  
  395.   while (lines)
  396.     {
  397.       lf->bufpos = (char *) memchr (lf->bufpos, '\n', lf->buflim - lf->bufpos);
  398.       if (! lf->bufpos)
  399.     {
  400.       ck_fwrite (start, lf->buflim - start, outfile);
  401.       if (! lf_refill (lf))
  402.         return;
  403.       start = lf->bufpos;
  404.     }
  405.       else
  406.     {
  407.       --lines;
  408.       ++lf->bufpos;
  409.     }
  410.     }
  411.  
  412.   ck_fwrite (start, lf->bufpos - start, outfile);
  413. }
  414.  
  415. /* Advance LINES on LF's infile without doing output */
  416. static void
  417. lf_skip (struct line_filter *lf, lin lines)
  418. {
  419.   while (lines)
  420.     {
  421.       lf->bufpos = (char *) memchr (lf->bufpos, '\n', lf->buflim - lf->bufpos);
  422.       if (! lf->bufpos)
  423.     {
  424.       if (! lf_refill (lf))
  425.         break;
  426.     }
  427.       else
  428.     {
  429.       --lines;
  430.       ++lf->bufpos;
  431.     }
  432.     }
  433. }
  434.  
  435. /* Snarf a line into a buffer.  Return EOF if EOF, 0 if error, 1 if OK.  */
  436. static int
  437. lf_snarf (struct line_filter *lf, char *buffer, size_t bufsize)
  438. {
  439.   for (;;)
  440.     {
  441.       char *start = lf->bufpos;
  442.       char *next = (char *) memchr (start, '\n', lf->buflim + 1 - start);
  443.       size_t s = next - start;
  444.       if (bufsize <= s)
  445.     return 0;
  446.       memcpy (buffer, start, s);
  447.       if (next < lf->buflim)
  448.     {
  449.       buffer[s] = 0;
  450.       lf->bufpos = next + 1;
  451.       return 1;
  452.     }
  453.       if (! lf_refill (lf))
  454.     return s ? 0 : EOF;
  455.       buffer += s;
  456.       bufsize -= s;
  457.     }
  458. }
  459.  
  460.  
  461.  
  462. int
  463. main (int argc, char *argv[])
  464. {
  465.   int opt;
  466.   char const *prog;
  467.  
  468. #ifdef __riscos
  469.   int tab_expand_flag = 0;
  470.   CHECK_TAB_EXPAND (tab_expand_flag);
  471. #endif
  472.  
  473.   exit_failure = EXIT_TROUBLE;
  474.   initialize_main (&argc, &argv);
  475.   program_name = argv[0];
  476.   setlocale (LC_ALL, "");
  477.   bindtextdomain (PACKAGE, LOCALEDIR);
  478.   textdomain (PACKAGE);
  479.   c_stack_action (c_stack_die);
  480.  
  481.   prog = getenv ("EDITOR");
  482.   if (prog)
  483.     editor_program = prog;
  484.  
  485.   diffarg (DEFAULT_DIFF_PROGRAM);
  486.  
  487.   /* parse command line args */
  488.   while ((opt = getopt_long (argc, argv, "abBdHiI:lo:stvw:W", longopts, 0))
  489.      != -1)
  490.     {
  491.       switch (opt)
  492.     {
  493.     case 'a':
  494.       diffarg ("-a");
  495.       break;
  496.  
  497.     case 'b':
  498.       diffarg ("-b");
  499.       break;
  500.  
  501.     case 'B':
  502.       diffarg ("-B");
  503.       break;
  504.  
  505.     case 'd':
  506.       diffarg ("-d");
  507.       break;
  508.  
  509.     case 'E':
  510.       diffarg ("-E");
  511.       break;
  512.  
  513.     case 'H':
  514.       diffarg ("-H");
  515.       break;
  516.  
  517.     case 'i':
  518.       diffarg ("-i");
  519.       break;
  520.  
  521.     case 'I':
  522.       diffarg ("-I");
  523.       diffarg (optarg);
  524.       break;
  525.  
  526.     case 'l':
  527.       diffarg ("--left-column");
  528.       break;
  529.  
  530.     case 'o':
  531.       output = optarg;
  532.       break;
  533.  
  534.     case 's':
  535.       suppress_common_lines = 1;
  536.       break;
  537.  
  538.     case 't':
  539. #ifdef __riscos
  540.       tab_expand_flag = 0;
  541. #endif
  542.       diffarg ("-t");
  543.       break;
  544.  
  545.     case 'v':
  546.       printf ("sdiff %s\n%s\n\n%s\n\n%s\n",
  547.           version_string, copyright_string,
  548.           _(free_software_msgid), _(authorship_msgid));
  549.       check_stdout ();
  550.       return EXIT_SUCCESS;
  551.  
  552.     case 'w':
  553.       diffarg ("-W");
  554.       diffarg (optarg);
  555.       break;
  556.  
  557.     case 'W':
  558.       diffarg ("-w");
  559.       break;
  560.  
  561.     case DIFF_PROGRAM_OPTION:
  562.       diffargv[0] = optarg;
  563.       break;
  564.  
  565.     case HELP_OPTION:
  566.       usage ();
  567.       check_stdout ();
  568.       return EXIT_SUCCESS;
  569.  
  570.     case STRIP_TRAILING_CR_OPTION:
  571.       diffarg ("--strip-trailing-cr");
  572.       break;
  573.  
  574.     default:
  575.       try_help (0, 0);
  576.     }
  577.     }
  578.  
  579.   if (argc - optind != 2)
  580.     {
  581.       if (argc - optind < 2)
  582.     try_help ("missing operand after `%s'", argv[argc - 1]);
  583.       else
  584.     try_help ("extra operand `%s'", argv[optind + 2]);
  585.     }
  586.  
  587.   if (! output)
  588.     {
  589.       /* easy case: diff does everything for us */
  590.       if (suppress_common_lines)
  591.     diffarg ("--suppress-common-lines");
  592.       diffarg ("-y");
  593.       diffarg ("--");
  594.       diffarg (argv[optind]);
  595.       diffarg (argv[optind + 1]);
  596.       diffarg (0);
  597.       execvp (diffargv[0], (char **) diffargv);
  598.       perror_fatal (diffargv[0]);
  599.     }
  600.   else
  601.     {
  602.       char const *lname, *rname;
  603.       FILE *left, *right, *out, *diffout;
  604.       bool interact_ok;
  605.       struct line_filter lfilt;
  606.       struct line_filter rfilt;
  607.       struct line_filter diff_filt;
  608.       bool leftdir = diraccess (argv[optind]);
  609.       bool rightdir = diraccess (argv[optind + 1]);
  610.  
  611.       if (leftdir & rightdir)
  612.     fatal ("both files to be compared are directories");
  613.  
  614.       lname = expand_name (argv[optind], leftdir, argv[optind + 1]);
  615.       left = ck_fopen (lname, "r");
  616.       rname = expand_name (argv[optind + 1], rightdir, argv[optind]);
  617.       right = ck_fopen (rname, "r");
  618.       out = ck_fopen (output, "w");
  619.  
  620.       diffarg ("--sdiff-merge-assist");
  621.       diffarg ("--");
  622.       diffarg (argv[optind]);
  623.       diffarg (argv[optind + 1]);
  624.       diffarg (0);
  625.  
  626.       trapsigs ();
  627.  
  628. #if ! (HAVE_WORKING_FORK || HAVE_WORKING_VFORK)
  629.       {
  630.     size_t cmdsize = 1;
  631.     char *p, *command;
  632.     int i;
  633.  
  634.     for (i = 0;  diffargv[i];  i++)
  635.       cmdsize += quote_system_arg (0, diffargv[i]) + 1;
  636.     command = p = xmalloc (cmdsize);
  637.     for (i = 0;  diffargv[i];  i++)
  638.       {
  639.         p += quote_system_arg (p, diffargv[i]);
  640.         *p++ = ' ';
  641.       }
  642.     p[-1] = 0;
  643.     errno = 0;
  644.     diffout = popen (command, "r");
  645.     if (! diffout)
  646.       perror_fatal (command);
  647.     free (command);
  648.       }
  649. #else
  650.       {
  651.     int diff_fds[2];
  652. # if HAVE_WORKING_VFORK
  653.     sigset_t procmask;
  654.     sigset_t blocked;
  655. # endif
  656.  
  657.     if (pipe (diff_fds) != 0)
  658.       perror_fatal ("pipe");
  659.  
  660. # if HAVE_WORKING_VFORK
  661.     /* Block SIGINT and SIGPIPE.  */
  662.     sigemptyset (&blocked);
  663.     sigaddset (&blocked, SIGINT);
  664.     sigaddset (&blocked, SIGPIPE);
  665.     sigprocmask (SIG_BLOCK, &blocked, &procmask);
  666. # endif
  667.     diffpid = vfork ();
  668.     if (diffpid < 0)
  669.       perror_fatal ("fork");
  670.     if (! diffpid)
  671.       {
  672.         /* Alter the child's SIGINT and SIGPIPE handlers;
  673.            this may munge the parent.
  674.            The child ignores SIGINT in case the user interrupts the editor.
  675.            The child does not ignore SIGPIPE, even if the parent does.  */
  676.         if (initial_handler (handler_index_of_SIGINT) != SIG_IGN)
  677.           signal_handler (SIGINT, SIG_IGN);
  678.         signal_handler (SIGPIPE, SIG_DFL);
  679. # if HAVE_WORKING_VFORK
  680.         /* Stop blocking SIGINT and SIGPIPE in the child.  */
  681.         sigprocmask (SIG_SETMASK, &procmask, 0);
  682. # endif
  683.         close (diff_fds[0]);
  684.         if (diff_fds[1] != STDOUT_FILENO)
  685.           {
  686.         dup2 (diff_fds[1], STDOUT_FILENO);
  687.         close (diff_fds[1]);
  688.           }
  689.  
  690.         execvp (diffargv[0], (char **) diffargv);
  691.         _exit (errno == ENOEXEC ? 126 : 127);
  692.       }
  693.  
  694. # if HAVE_WORKING_VFORK
  695.     /* Restore the parent's SIGINT and SIGPIPE behavior.  */
  696.     if (initial_handler (handler_index_of_SIGINT) != SIG_IGN)
  697.       signal_handler (SIGINT, catchsig);
  698.     if (initial_handler (handler_index_of_SIGPIPE) != SIG_IGN)
  699.       signal_handler (SIGPIPE, catchsig);
  700.     else
  701.       signal_handler (SIGPIPE, SIG_IGN);
  702.  
  703.     /* Stop blocking SIGINT and SIGPIPE in the parent.  */
  704.     sigprocmask (SIG_SETMASK, &procmask, 0);
  705. # endif
  706.  
  707.     close (diff_fds[1]);
  708.     diffout = fdopen (diff_fds[0], "r");
  709.     if (! diffout)
  710.       perror_fatal ("fdopen");
  711.       }
  712. #endif
  713.  
  714.       lf_init (&diff_filt, diffout);
  715.       lf_init (&lfilt, left);
  716.       lf_init (&rfilt, right);
  717.  
  718.       interact_ok = interact (&diff_filt, &lfilt, lname, &rfilt, rname, out);
  719.  
  720.       ck_fclose (left);
  721.       ck_fclose (right);
  722.       ck_fclose (out);
  723.  
  724.       {
  725.     int wstatus;
  726.     int werrno = 0;
  727.  
  728. #if ! (HAVE_WORKING_FORK || HAVE_WORKING_VFORK)
  729.     wstatus = pclose (diffout);
  730.     if (wstatus == -1)
  731.       werrno = errno;
  732. #else
  733.     ck_fclose (diffout);
  734.     while (waitpid (diffpid, &wstatus, 0) < 0)
  735.       if (errno == EINTR)
  736.         checksigs ();
  737.       else
  738.         perror_fatal ("waitpid");
  739.     diffpid = 0;
  740. #endif
  741.  
  742.     if (tmpname)
  743.       {
  744.         unlink (tmpname);
  745.         tmpname = 0;
  746.       }
  747.  
  748.     if (! interact_ok)
  749.       exiterr ();
  750.  
  751.     ck_editor_status (werrno, wstatus);
  752.     untrapsig (0);
  753.     checksigs ();
  754.     exit (WEXITSTATUS (wstatus));
  755.       }
  756.     }
  757.   return EXIT_SUCCESS;            /* Fool `-Wall'.  */
  758. }
  759.  
  760. static void
  761. diffarg (char const *a)
  762. {
  763.   static size_t diffargs, diffarglim;
  764.  
  765.   if (diffargs == diffarglim)
  766.     {
  767.       if (! diffarglim)
  768.     diffarglim = 16;
  769.       else if (PTRDIFF_MAX / (2 * sizeof *diffargv) <= diffarglim)
  770.     xalloc_die ();
  771.       else
  772.     diffarglim *= 2;
  773.       diffargv = xrealloc (diffargv, diffarglim * sizeof *diffargv);
  774.     }
  775.   diffargv[diffargs++] = a;
  776. }
  777.  
  778.  
  779.  
  780.  
  781. /* Signal handling */
  782.  
  783. static bool volatile ignore_SIGINT;
  784. static int volatile signal_received;
  785. static bool sigs_trapped;
  786.  
  787. static RETSIGTYPE
  788. catchsig (int s)
  789. {
  790. #if ! HAVE_SIGACTION
  791.   signal (s, SIG_IGN);
  792. #endif
  793.   if (! (s == SIGINT && ignore_SIGINT))
  794.     signal_received = s;
  795. }
  796.  
  797. #if HAVE_SIGACTION
  798. static struct sigaction catchaction;
  799.  
  800. static void
  801. signal_handler (int sig, RETSIGTYPE (*handler) (int))
  802. {
  803.   catchaction.sa_handler = handler;
  804.   sigaction (sig, &catchaction, 0);
  805. }
  806. #endif
  807.  
  808. static void
  809. trapsigs (void)
  810. {
  811.   int i;
  812.  
  813. #if HAVE_SIGACTION
  814.   catchaction.sa_flags = SA_RESTART;
  815.   sigemptyset (&catchaction.sa_mask);
  816.   for (i = 0;  i < NUM_SIGS;  i++)
  817.     sigaddset (&catchaction.sa_mask, sigs[i]);
  818. #endif
  819.  
  820.   for (i = 0;  i < NUM_SIGS;  i++)
  821.     {
  822. #if HAVE_SIGACTION
  823.       sigaction (sigs[i], 0, &initial_action[i]);
  824. #else
  825.       initial_action[i] = signal (sigs[i], SIG_IGN);
  826. #endif
  827.       if (initial_handler (i) != SIG_IGN)
  828.     signal_handler (sigs[i], catchsig);
  829.     }
  830.  
  831. #ifdef SIGCHLD
  832.   /* System V fork+wait does not work if SIGCHLD is ignored.  */
  833.   signal (SIGCHLD, SIG_DFL);
  834. #endif
  835.  
  836.   sigs_trapped = 1;
  837. }
  838.  
  839. /* Untrap signal S, or all trapped signals if S is zero.  */
  840. static void
  841. untrapsig (int s)
  842. {
  843.   int i;
  844.  
  845.   if (sigs_trapped)
  846.     for (i = 0;  i < NUM_SIGS;  i++)
  847.       if ((! s || sigs[i] == s)  &&  initial_handler (i) != SIG_IGN)
  848. #if HAVE_SIGACTION
  849.       sigaction (sigs[i], &initial_action[i], 0);
  850. #else
  851.       signal (sigs[i], initial_action[i]);
  852. #endif
  853. }
  854.  
  855. /* Exit if a signal has been received.  */
  856. static void
  857. checksigs (void)
  858. {
  859.   int s = signal_received;
  860.   if (s)
  861.     {
  862.       cleanup ();
  863.  
  864.       /* Yield an exit status indicating that a signal was received.  */
  865.       untrapsig (s);
  866.       kill (getpid (), s);
  867.  
  868.       /* That didn't work, so exit with error status.  */
  869.       exit (EXIT_TROUBLE);
  870.     }
  871. }
  872.  
  873.  
  874. static void
  875. give_help (void)
  876. {
  877.   fprintf (stderr, "%s", _("\
  878. ed:     Edit then use both versions, each decorated with a header.\n\
  879. eb:     Edit then use both versions.\n\
  880. el:     Edit then use the left version.\n\
  881. er:     Edit then use the right version.\n\
  882. e:      Edit a new version.\n\
  883. l:      Use the left version.\n\
  884. r:      Use the right version.\n\
  885. s:      Silently include common lines.\n\
  886. v:      Verbosely include common lines.\n\
  887. q:      Quit.\n\
  888. "));
  889. }
  890.  
  891. static int
  892. skip_white (void)
  893. {
  894.   int c;
  895.   for (;;)
  896.     {
  897.       c = getchar ();
  898.       if (! ISSPACE (c) || c == '\n')
  899.     break;
  900.       checksigs ();
  901.     }
  902.   if (ferror (stdin))
  903.     perror_fatal (_("read failed"));
  904.   return c;
  905. }
  906.  
  907. static void
  908. flush_line (void)
  909. {
  910.   int c;
  911.   while ((c = getchar ()) != '\n' && c != EOF)
  912.     continue;
  913.   if (ferror (stdin))
  914.     perror_fatal (_("read failed"));
  915. }
  916.  
  917.  
  918. /* interpret an edit command */
  919. static bool
  920. edit (struct line_filter *left, char const *lname, lin lline, lin llen,
  921.       struct line_filter *right, char const *rname, lin rline, lin rlen,
  922.       FILE *outfile)
  923. {
  924.   for (;;)
  925.     {
  926.       int cmd0=0, cmd1=0;
  927.       bool gotcmd = 0;
  928.  
  929.       cmd1 = 0; /* Pacify `gcc -W'.  */
  930.  
  931.       while (! gotcmd)
  932.     {
  933.       if (putchar ('%') != '%')
  934.         perror_fatal (_("write failed"));
  935.       ck_fflush (stdout);
  936.  
  937.       cmd0 = skip_white ();
  938.       switch (cmd0)
  939.         {
  940.         case 'l': case 'r': case 's': case 'v': case 'q':
  941.           if (skip_white () != '\n')
  942.         {
  943.           give_help ();
  944.           flush_line ();
  945.           continue;
  946.         }
  947.           gotcmd = 1;
  948.           break;
  949.  
  950.         case 'e':
  951.           cmd1 = skip_white ();
  952.           switch (cmd1)
  953.         {
  954.         case 'b': case 'd': case 'l': case 'r':
  955.           if (skip_white () != '\n')
  956.             {
  957.               give_help ();
  958.               flush_line ();
  959.               continue;
  960.             }
  961.           gotcmd = 1;
  962.           break;
  963.         case '\n':
  964.           gotcmd = 1;
  965.           break;
  966.         default:
  967.           give_help ();
  968.           flush_line ();
  969.           continue;
  970.         }
  971.           break;
  972.  
  973.         case EOF:
  974.           if (feof (stdin))
  975.         {
  976.           gotcmd = 1;
  977.           cmd0 = 'q';
  978.           break;
  979.         }
  980.           /* Fall through.  */
  981.         default:
  982.           flush_line ();
  983.           /* Fall through.  */
  984.         case '\n':
  985.           give_help ();
  986.           continue;
  987.         }
  988.     }
  989.  
  990.       switch (cmd0)
  991.     {
  992.     case 'l':
  993.       lf_copy (left, llen, outfile);
  994.       lf_skip (right, rlen);
  995.       return 1;
  996.     case 'r':
  997.       lf_copy (right, rlen, outfile);
  998.       lf_skip (left, llen);
  999.       return 1;
  1000.     case 's':
  1001.       suppress_common_lines = 1;
  1002.       break;
  1003.     case 'v':
  1004.       suppress_common_lines = 0;
  1005.       break;
  1006.     case 'q':
  1007.       return 0;
  1008.     case 'e':
  1009.       {
  1010.         int fd;
  1011.  
  1012.         if (tmpname)
  1013.           tmp = fopen (tmpname, "w");
  1014.         else
  1015.           {
  1016.         if ((fd = temporary_file ()) < 0)
  1017.           perror_fatal ("mkstemp");
  1018.         tmp = fdopen (fd, "w");
  1019.           }
  1020.  
  1021.         if (! tmp)
  1022.           perror_fatal (tmpname);
  1023.  
  1024.         switch (cmd1)
  1025.           {
  1026.           case 'd':
  1027.         if (llen)
  1028.           {
  1029.             if (llen == 1)
  1030.               fprintf (tmp, "--- %s %ld\n", lname, (long) lline);
  1031.             else
  1032.               fprintf (tmp, "--- %s %ld,%ld\n", lname,
  1033.                    (long) lline, (long) (lline + llen - 1));
  1034.           }
  1035.         /* Fall through.  */
  1036.           case 'b': case 'l':
  1037.         lf_copy (left, llen, tmp);
  1038.         break;
  1039.  
  1040.           default:
  1041.         lf_skip (left, llen);
  1042.         break;
  1043.           }
  1044.  
  1045.         switch (cmd1)
  1046.           {
  1047.           case 'd':
  1048.         if (rlen)
  1049.           {
  1050.             if (rlen == 1)
  1051.               fprintf (tmp, "+++ %s %ld\n", rname, (long) rline);
  1052.             else
  1053.               fprintf (tmp, "+++ %s %ld,%ld\n", rname,
  1054.                    (long) rline, (long) (rline + rlen - 1));
  1055.           }
  1056.         /* Fall through.  */
  1057.           case 'b': case 'r':
  1058.         lf_copy (right, rlen, tmp);
  1059.         break;
  1060.  
  1061.           default:
  1062.         lf_skip (right, rlen);
  1063.         break;
  1064.           }
  1065.  
  1066.         ck_fclose (tmp);
  1067.  
  1068.         {
  1069.           int wstatus;
  1070.           int werrno = 0;
  1071.           ignore_SIGINT = 1;
  1072.           checksigs ();
  1073.  
  1074.           {
  1075. #if ! (HAVE_WORKING_FORK || HAVE_WORKING_VFORK)
  1076.         char *command =
  1077.           xmalloc (quote_system_arg (0, editor_program)
  1078.                + 1 + strlen (tmpname) + 1);
  1079.         sprintf (command + quote_system_arg (command, editor_program),
  1080.              " %s", tmpname);
  1081.         wstatus = system (command);
  1082.         if (wstatus == -1)
  1083.           werrno = errno;
  1084.         free (command);
  1085. #else
  1086.         pid_t pid;
  1087.  
  1088.         pid = vfork ();
  1089.         if (pid == 0)
  1090.           {
  1091.             char const *argv[3];
  1092.             int i = 0;
  1093.  
  1094.             argv[i++] = editor_program;
  1095.             argv[i++] = tmpname;
  1096.             argv[i] = 0;
  1097.  
  1098.             execvp (editor_program, (char **) argv);
  1099.             _exit (errno == ENOEXEC ? 126 : 127);
  1100.           }
  1101.  
  1102.         if (pid < 0)
  1103.           perror_fatal ("fork");
  1104.  
  1105.         while (waitpid (pid, &wstatus, 0) < 0)
  1106.           if (errno == EINTR)
  1107.             checksigs ();
  1108.           else
  1109.             perror_fatal ("waitpid");
  1110. #endif
  1111.           }
  1112.  
  1113.           ignore_SIGINT = 0;
  1114.           ck_editor_status (werrno, wstatus);
  1115.         }
  1116.  
  1117.         {
  1118.           char buf[SDIFF_BUFSIZE];
  1119.           size_t size;
  1120.           tmp = ck_fopen (tmpname, "r");
  1121.           while ((size = ck_fread (buf, SDIFF_BUFSIZE, tmp)) != 0)
  1122.         {
  1123.           checksigs ();
  1124.           ck_fwrite (buf, size, outfile);
  1125.         }
  1126.           ck_fclose (tmp);
  1127.         }
  1128.         return 1;
  1129.       }
  1130.     default:
  1131.       give_help ();
  1132.       break;
  1133.     }
  1134.     }
  1135. }
  1136.  
  1137.  
  1138.  
  1139. /* Alternately reveal bursts of diff output and handle user commands.  */
  1140. static bool
  1141. interact (struct line_filter *diff,
  1142.       struct line_filter *left, char const *lname,
  1143.       struct line_filter *right, char const *rname,
  1144.       FILE *outfile)
  1145. {
  1146.   lin lline = 1, rline = 1;
  1147.  
  1148.   for (;;)
  1149.     {
  1150.       char diff_help[256];
  1151.       int snarfed = lf_snarf (diff, diff_help, sizeof diff_help);
  1152.  
  1153.       if (snarfed <= 0)
  1154.     return snarfed != 0;
  1155.  
  1156.       checksigs ();
  1157.  
  1158.       if (diff_help[0] == ' ')
  1159.     puts (diff_help + 1);
  1160.       else
  1161.     {
  1162.       char *numend;
  1163.       uintmax_t val;
  1164.       lin llen, rlen, lenmax;
  1165.       errno = 0;
  1166.       llen = val = strtoumax (diff_help + 1, &numend, 10);
  1167.       if (llen < 0 || llen != val || errno || *numend != ',')
  1168.         fatal (diff_help);
  1169.       rlen = val = strtoumax (numend + 1, &numend, 10);
  1170.       if (rlen < 0 || rlen != val || errno || *numend)
  1171.         fatal (diff_help);
  1172.  
  1173.       lenmax = MAX (llen, rlen);
  1174.  
  1175.       switch (diff_help[0])
  1176.         {
  1177.         case 'i':
  1178.           if (suppress_common_lines)
  1179.         lf_skip (diff, lenmax);
  1180.           else
  1181.         lf_copy (diff, lenmax, stdout);
  1182.  
  1183.           lf_copy (left, llen, outfile);
  1184.           lf_skip (right, rlen);
  1185.           break;
  1186.  
  1187.         case 'c':
  1188.           lf_copy (diff, lenmax, stdout);
  1189.           if (! edit (left, lname, lline, llen,
  1190.               right, rname, rline, rlen,
  1191.               outfile))
  1192.         return 0;
  1193.           break;
  1194.  
  1195.         default:
  1196.           fatal (diff_help);
  1197.         }
  1198.  
  1199.       lline += llen;
  1200.       rline += rlen;
  1201.     }
  1202.     }
  1203. }
  1204.  
  1205. /* Return nonzero if DIR is an existing directory.  */
  1206. static bool
  1207. diraccess (char const *dir)
  1208. {
  1209.   struct stat buf;
  1210.   return stat (dir, &buf) == 0 && S_ISDIR (buf.st_mode);
  1211. }
  1212.  
  1213. #ifndef P_tmpdir
  1214. # define P_tmpdir "/tmp"
  1215. #endif
  1216. #ifndef TMPDIR_ENV
  1217. # define TMPDIR_ENV "TMPDIR"
  1218. #endif
  1219.  
  1220. /* Open a temporary file and return its file descriptor.  Put into
  1221.    tmpname the address of a newly allocated buffer that holds the
  1222.    file's name.  Use the prefix "sdiff".  */
  1223. static int
  1224. temporary_file (void)
  1225. {
  1226. #ifdef __riscos
  1227.   return fileno (tmpfile ());
  1228. #else
  1229.   char const *tmpdir = getenv (TMPDIR_ENV);
  1230.   char const *dir = tmpdir ? tmpdir : P_tmpdir;
  1231.   char *buf = xmalloc (strlen (dir) + 1 + 5 + 6 + 1);
  1232.   int fd;
  1233.   int e;
  1234.   sigset_t procmask;
  1235.   sigset_t blocked;
  1236.   sprintf (buf, "%s/sdiffXXXXXX", dir);
  1237.   sigemptyset (&blocked);
  1238.   sigaddset (&blocked, SIGINT);
  1239.   sigprocmask (SIG_BLOCK, &blocked, &procmask);
  1240.   fd = mkstemp (buf);
  1241.   e = errno;
  1242.   if (0 <= fd)
  1243.     tmpname = buf;
  1244.   sigprocmask (SIG_SETMASK, &procmask, 0);
  1245.   errno = e;
  1246.   return fd;
  1247. #endif
  1248. }
  1249.